﻿/******************************************************************************
 *
 * @copyright:          Copyright 2011 UniSky Ltd.
 * @file:
 * @author:              kenping.liu
 * @creation:
 * @description:
 *
 * @modifications
 * date         user                description
 *
 *
 * @more-details
 *
 *****************************************************************************/
#ifndef _UNISKY_UTIL_STR_H_
#define _UNISKY_UTIL_STR_H_

#include "../u_inc.h"

U_NAMESPACE_BEGIN(u)
U_NAMESPACE_BEGIN(util)

#if defined(LINUX)
// 代码转换操作类
class CodeConverter
{
public:
    // 构造
    CodeConverter(const char *from_charset, const char *to_charset)
    {
        _cd = iconv_open(to_charset, from_charset);
    }

    // 析构
    ~CodeConverter()
    {
        if ( 0!=_cd )
        {
            iconv_close(_cd);
        }
    }

    // 转换输出
    bool convert(u::AutoHeap& out, char* inbuf, size_t insize)
    {
        size_t inlen  = insize;
        size_t outlen = inlen*2;
        u::AutoHeap tmp(outlen);
        char **pin  = &inbuf;
        char *p = tmp.head();
        char **pout = &p;

        if ( ((size_t)-1) != iconv(_cd, pin, &inlen, pout, &outlen) )
        {
            outlen = tmp.size()-outlen;
            out.reset(outlen, tmp.head(), outlen);
            return true;
        }
        return false;
    }
private:
    iconv_t _cd;
};
#endif

class Str
{
public:
    typedef std::vector<std::string>            Array;
    typedef Array::iterator                     iterator;
    typedef std::pair<std::string, std::string> pair;
    typedef std::vector<pair>::iterator         pair_iter;

    //
    // from: http://www.cse.yorku.ca/~oz/hash.html
    //
    // Java String's hash algorithm
    // h = 0;
    // for (int i = 0; i < len; i++)
    // {
    //      h = 31*h + str[i];
    // }
    static unsigned long hash(const char* _Str)
    {
        unsigned long  hash_ = 5381;
        unsigned char* str   = (unsigned char*)_Str;
        int                  c;

        while ( 0 != (c=*str++) )
        {
            hash_ = ((hash_ << 5) + hash_) + c; /* hash_ * 33 + c */
        }

        return hash_;
    }

    static bool equals(const TCHAR* str1, const TCHAR* str2)
    {
#if defined(WINDOWS)
        return 0==_tcscmp(str1, str2);
#else
        return 0==strcmp(str1, str2);
#endif
    }

    static bool equals(const TCHAR* str1, const TCHAR* str2, size_t len)
    {
        for ( size_t i=0; i<len; i++ )
        {
            if ( *(str1+i) != *(str2+i) )
            {
                return false;
            }
        }
        return true;
    }

    //
    // 删除字符串起始部分的空白字符或指定字符
    //
    static void trim_left(std::string& str, const char* white_space=" \t\r\n")
    {
        std::string::size_type pos = str.find_first_not_of(white_space);
        if(pos != std::string::npos)
        {
            str.erase(0, pos);
        }
    };

    //
    // 删除字符串结束部分的空白字符或指定字符
    //
    static void trim_right(std::string& str, const char* white_space=" \t\r\n")
    {
        std::string::size_type pos = str.find_last_not_of(white_space);
        if(pos != std::string::npos)
        {
            str.erase(pos + 1);
        }
    };

    //
    // 删除字符串两端的空白字符或指定字符
    //
    static void trim(std::string& str, const char* white_space=" \t\r\n")
    {
        std::string::size_type pos = str.find_last_not_of(white_space);
        if(pos != std::string::npos)
        {
            str.erase(pos + 1);
            pos = str.find_first_not_of(white_space);
            if(pos != std::string::npos)
            {
                str.erase(0, pos);
            }
        }
        else
        {
            str.erase(str.begin(), str.end());
        }
    };

    //
    // 按指定的分隔符将vector合并成字符串
    //
    template<typename T>
    static std::string join(std::vector<T>& v, const std::string& token=",")
    {
        std::stringstream result;

        for ( typename std::vector<T>::iterator it=v.begin(); it!=v.end(); it++ )
        {
            if ( it!=v.begin() )
            {
                result<<token;
            }
            result<<*it;
        }

        return result.str();
    }

    //
    // 按指定的分隔符将数组合并成字符串
    //
    template<typename T>
    static std::string join(T arr[], size_t size, std::string splitter=",")
    {
        std::stringstream result;

        for ( size_t i=0; i<size; i++ )
        {
            if ( i>0 )
            {
                result<<splitter;
            }
            result<<arr[i];
        }

        return result.str();
    }

    //
    // 用指定的分隔符分隔字符串
    //
    // utilities functions
    static int split(Array&             results,
                     const std::string& input,
                     const std::string& token,
                     bool               includeEmpties=true)
    {
        results.clear();

        std::string::size_type szTotal = input.size();
        std::string::size_type szTk = token.size();

        std::string::size_type lpos = 0;
        std::string::size_type rpos = input.find(token, lpos);
        while ( rpos!=std::string::npos && lpos<szTotal)
        {
            std::string str = input.substr(lpos, rpos-lpos);
            if ( !str.empty() || includeEmpties )
            {
                results.push_back(str);
            }

            lpos = rpos+szTk;
            rpos = input.find(token, lpos);
        }

        if ( lpos<szTotal )
        {
            std::string str = input.substr(lpos, szTotal-lpos);
            if ( !str.empty() || includeEmpties )
            {
                results.push_back(str);
            }
        }

        return 0;
    };

    //
    // 用指定的分隔符分隔字符串，对所有结果会trim
    //
    // utilities functions
    static int split_ex(Array&             results,
                        const std::string& input,
                        const std::string& token,
                        bool               includeEmpties=true)
    {
        results.clear();

        std::string::size_type szTotal = input.size();
        std::string::size_type szTk = token.size();

        std::string::size_type lpos = 0;
        std::string::size_type rpos = input.find(token, lpos);
        while ( rpos!=std::string::npos && lpos<szTotal)
        {
            std::string str = input.substr(lpos, rpos-lpos);
            trim(str);
            if ( !str.empty() || includeEmpties )
            {
                results.push_back(str);
            }

            lpos = rpos+szTk;
            rpos = input.find(token, lpos);
        }

        if ( lpos<szTotal )
        {
            std::string str = input.substr(lpos, szTotal-lpos);
            trim(str);
            if ( !str.empty() || includeEmpties )
            {
                results.push_back(str);
            }
        }

        return 0;
    };

    static void replace(std::string& str, char srch, char rep)
    {
        if ( srch==rep )
        {
            return;
        }

        for ( std::string::size_type i=0; i<str.size(); i++ )
        {
            if ( str.at(i) == srch )
            {
                str[i] = rep;
            }
        }
    }

    static void wreplace(std::wstring& str, wchar_t srch, wchar_t rep)
    {
        if ( srch==rep )
        {
            return;
        }
        for ( std::wstring::size_type i=0; i<str.size(); i++ )
        {
            if ( str.at(i) == srch )
            {
                str[i] = rep;
            }
        }
    }

    static void replace(std::string& str, const char* srch, const char* rep)
    {
        if ( 0==strcmp(srch, rep) )
        {
            return;
        }
        std::string::size_type pos = 0;
        while ( (pos = str.find(srch, pos)) != std::string::npos )
        {
            str.replace( pos, strlen(srch), rep);
            pos++;
        }
    }

    static void replace(std::string& str, const std::string& srch, std::string& rep)
    {
        if ( srch==rep )
        {
            return;
        }
        std::string::size_type pos = 0;
        while ( (pos = str.find(srch, pos)) != std::string::npos )
        {
            str.replace( pos, srch.size(), rep);
            pos++;
        }
    }

    /**
    * Converts any class with a proper overload of the << opertor to a std::string
    * @param value The value to be converted
    * @throws Exception When value cannot be converted to a std::string
    */
    template <typename T>
    static std::string to(const T& value)
    {
        std::stringstream convert;
        convert << value;
        if ( convert.fail() )
        {
            //O2S_THROW( "Could not convert value to text" );
        }
        return convert.str();
    };

    static std::string to(const std::string& value)
    {
        return value;
    };

    /**
    * Converts a std::string to any class with a proper overload of the >> opertor
    * @param temp         The string to be converted
    * @param out  [OUT]   The container for the returned value
    * @throws Exception When temp cannot be converted to the target type
    */
    template <typename T>
    static void from(const std::string& str, T* out)
    {
        if ( NULL!=out )
        {
            std::istringstream val( str );
            val >> *out;

            if ( val.fail() )
            {
                //O2S_THROW( "Could not convert \"" << str << "\" to target type" );
                ZeroMemory(out, sizeof(T));
            }
        }
    };

    /**
    * Specialization for std::string
    */
    static void from(const std::string& temp, std::string* out)
    {
        *out = temp;
    };

    static std::string format(const char *fmt, ...)
    {
        AutoHeap buf(4096);
        va_list marker;
        va_start(marker, fmt);
#if defined(MSC_2008)
        u_vsprintf(buf, buf.length(), fmt, marker);
#else
        u_vsprintf(buf, fmt, marker);
#endif
        va_end(marker);
        return std::string(buf);
    }

    static void append(std::string& str, const char *fmt, ...)
    {
        AutoHeap buf(40960);
        va_list marker;
        va_start(marker, fmt);
#if defined(MSC_2008)
        u_vsprintf(buf, buf.length(), fmt, marker);
#else
        u_vsprintf(buf, fmt, marker);
#endif
        va_end(marker);
        str.append(buf);
    }
    
#if defined(WINDOWS)
    static void w_to_a(char* out, size_t& a_cnt, const wchar_t* wstr, size_t w_cnt)
    {
        AutoHeap tmp(1);
        w_to_a(tmp, wstr, w_cnt);
        a_cnt = tmp.length();
        memcpy(out, tmp.head(), tmp.size());
    }

    static void w_to_a(AutoHeap& out, const wchar_t* wstr, size_t w_cnt)
    {
        size_t a_cnt = WideCharToMultiByte(CP_ACP, 0, wstr, w_cnt, 0, 0, 0, 0);
        out.reset(a_cnt);
        WideCharToMultiByte(CP_ACP, 0, wstr, w_cnt, out.head(), a_cnt, 0, 0);
    }

    static void a_to_w(wchar_t* out, size_t& w_cnt, const char* str, size_t a_cnt)
    {
        AutoHeapW wtmp(1);
        a_to_w(wtmp, str, a_cnt);
        w_cnt = wtmp.length();
        memcpy(out, wtmp.head(), wtmp.size());
    }

    static void a_to_w(AutoHeapW& out, const char* str, size_t a_cnt)
    {
        size_t w_cnt = MultiByteToWideChar(CP_ACP, 0, str, a_cnt, 0, 0);
        out.reset(w_cnt);
        MultiByteToWideChar(CP_ACP, 0, str, a_cnt, out.head(), w_cnt);
    }

    static void utf8_to_a(AutoHeap& out, const char* ustr, size_t u_len)
    {
        int wlen = MultiByteToWideChar(CP_UTF8, 0, ustr, -1, 0, 0);
        AutoHeapW wsz(wlen);
        MultiByteToWideChar(CP_UTF8, 0, ustr, -1, wsz, wlen);

        size_t alen = WideCharToMultiByte(CP_ACP, 0, wsz, wlen, 0, 0, 0, 0);
        out.reset(alen);
        WideCharToMultiByte(CP_ACP, 0, wsz, wlen, out, alen, 0, 0);
    }

    static void a_to_utf8(AutoHeap& out, const char* astr, size_t a_len)
    {    
        int wlen = MultiByteToWideChar(CP_ACP, 0, astr, -1, 0, 0);
        AutoHeapW wsz(wlen);
        MultiByteToWideChar(CP_ACP, 0, astr, -1, wsz, wlen);

        size_t alen = WideCharToMultiByte(CP_UTF8, 0, wsz, wlen, 0, 0, 0, 0);
        out.reset(alen);
        WideCharToMultiByte(CP_UTF8, 0, wsz, wlen, out, alen, 0, 0); 
    }
#else
    static void w_to_a(char* out, size_t& a_cnt, const wchar_t* wstr, size_t w_cnt)
    {
        AutoHeap tmp(1);
        w_to_a(tmp, wstr, w_cnt);
        a_cnt = tmp.length();
        memcpy(out, tmp.head(), tmp.size());
    }

    static void w_to_a(AutoHeap& out, const wchar_t* wstr, size_t w_cnt)
    {
        CodeConverter cc("utf-16","gbk");
        cc.convert(out, (char*)wstr, w_cnt*sizeof(wchar_t));
    }

    static void a_to_w(wchar_t* out, size_t& w_cnt, const char* str, size_t a_cnt)
    {
        AutoHeapW wtmp(1);
        a_to_w(wtmp, str, a_cnt);
        w_cnt = wtmp.length();
        memcpy(out, wtmp.head(), wtmp.size());
    }

    static void a_to_w(AutoHeapW& _Out, const char* str, size_t a_cnt)
    {
        CodeConverter cc("gbk","utf-16");
        AutoHeap out(10);
        cc.convert(out, (char*)str, a_cnt);
        size_t len = out.size()/sizeof(wchar_t);
        _Out.reset(len, (wchar_t*)out.head(), len);
    }

    static void utf8_to_a(AutoHeap& out, const char* ustr, size_t u_len)
    {
        CodeConverter cc("utf-8","gbk");
        cc.convert(out, (char*)ustr, u_len);
    }

    static void a_to_utf8(AutoHeap& out, const char* astr, size_t a_len)
    {
        CodeConverter cc("gbk", "utf-8");
        cc.convert(out, (char*)astr, a_len);
    }
#endif
};

U_NAMESPACE_END(util)
U_NAMESPACE_END(u)

#endif
